#include "TestSsytrd.h"
#include <vector>
#include <float.h>
#include <cmath>
#include <algorithm>
#include <CL/cl.h>

#include "../../lapack/f2c.h"
#include "Platform.h"
#include "../../Profiler.h"
#include "../../FormatString.h"
#include "../../LapackPlatform/Lapack.h"
#include "../../OclLapack/OclLapack.h"
#include "TestPlatform.h"
#include "../common/TestUtils.h"

CPPUNIT_TEST_SUITE_REGISTRATION(TestSsytrd);

using namespace std;

inline void CHECK_LAPACK(int info) {
	CPPUNIT_ASSERT(info == 0);
}

TestSsytrd::TestSsytrd() {
}

TestSsytrd::~TestSsytrd() {
}

void TestSsytrd::setUp() {
}

void TestSsytrd::tearDown() {
}

void TestSsytrd::testKnownTridiagonalOnly() {
    
    real matrixData[] = {
          1.36, -0.816,  0.521,   1.43, -0.144,
        -0.816, -0.659,  0.794, -0.173, -0.406,
        0.521,  0.794, -0.541,  0.461,  0.179,
        1.43, -0.173,  0.461,  -1.43,  0.822,
        -0.144, -0.406,  0.179,  0.822,  -1.37
    };
    
    real d[5], e[5], tau[5];
    
    integer size = 5;
    integer lwork = -1;
    integer info = 0;
    
    Lapack lapack;
    
    real temp;    
    lapack.ssytrd("L", &size, NULL, &size, NULL, NULL, NULL, &temp, &lwork, &info);
    lwork = (integer)temp;
    
    vector<real> work(lwork);    
    lapack.ssytrd("L", &size, matrixData, &size, d, e, tau, &work[0], &lwork, &info);
	CHECK_LAPACK(info);
        
    real correctD[] = { 1.36f, -1.193342f, -1.281203f, -1.688568f, 0.163113f };
    real correctE[] = { 1.732897f, -0.965331f, 0.213806f, 0.347195f };
    
    CPPUNIT_ASSERT(compare(d, correctD, size, 1E-5f) == 0);    
    CPPUNIT_ASSERT(compare(e, correctE, size - 1, 1E-5f) == 0);
}

void TestSsytrd::testLapackTridiagonalization() {
    srand(102030);
    Lapack lapack;
    TestPlatform test(lapack);
    for (int size = 200; size <= 400; size+= 200) {
        test.testTridiagonalization(size);
    }
}

void testOclTridiagonalization(cl_device_type deviceType) {
	srand(102030);
	
	try {
		OclLapack lapack(deviceType);
		TestPlatform test(lapack);
		for (int size = 200; size <= 400; size+= 200) {
			test.testTridiagonalization(size);
		}
	} catch (Exception& e) {
		CPPUNIT_ASSERT_MESSAGE(e.what(), false);
	}
}

void TestSsytrd::testOclTridiagonalizationCPU() {
    testOclTridiagonalization(CL_DEVICE_TYPE_CPU);
}

void TestSsytrd::testOclTridiagonalizationGPU() {
    testOclTridiagonalization(CL_DEVICE_TYPE_GPU);
}

